// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: daniel

#include "coin2/exchange/upbit/api_util/auth_key.h"

#include <sstream>
#include <string>

#include <jwt-cpp/jwt.h>

#include "coin2/base/time.h"
#include "coin2/exchange/base/api_util/api_signature.h"
#include "coin2/exchange/base/api_util/json_writer.h"
#include "coin2/exchange/base/api_util/nonce_manager.h"

std::string UpbitBaseAuth::GetAuth(const std::string& query_str) const {
  auto nonce = impl::NonceManager::Get();
  if (query_str.empty()) {
    return prefix_str_ + jwt::create()
                             .set_type(std::string("JWT"))
                             .set_algorithm(std::string("HS256"))
                             .set_payload_claim("access_key", jwt::claim(access_key_))
                             .set_payload_claim("nonce", jwt::claim(std::to_string(nonce)))
                             .sign(jwt::algorithm::hs256{secret_key_});
  } else {
    return prefix_str_ + jwt::create()
                             .set_type(std::string("JWT"))
                             .set_algorithm(std::string("HS256"))
                             .set_payload_claim("access_key", jwt::claim(access_key_))
                             .set_payload_claim("nonce", jwt::claim(std::to_string(nonce)))
                             .set_payload_claim(
                                 "query_hash",
                                 jwt::claim(ApiSignatureUtil::GetHashByHexSHA512(query_str)))
                             .set_payload_claim("query_hash_alg", jwt::claim(hash_method_))
                             .sign(jwt::algorithm::hs256{secret_key_});
  }
}

auto UpbitBaseAuth::GetHeaderSet(const std::string& query_str) -> const HeaderSet& {
  CHECK_THROW(!header_set_.empty()) << "Header set is empty";
  header_set_[0].second = GetAuth(query_str);
  return header_set_;
}

AsioHttpContext* UpbitRestAuth::CreateRequestByGetWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  CHECK_THROW(builder->ComputePostBody().empty()) << "It is GET Method, No Payload";
  std::string target = address;
  auto external_query_string = GetExternalQueryString();
  if (!external_query_string.empty()) {
    if (!query_string.empty()) {
      query_string.append("&");
    }
    query_string.append(external_query_string);
  }
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  auto ahc = AsioHttpContext::NewGet(host_, port_, bind_ip_, target, GetHeaderSet(query_string));
  ahc->keep_user_agent = true;

  return ahc;
}

AsioHttpContext* UpbitRestAuth::CreateRequestByPostWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = builder->ComputePostBody();
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  auto ahc = AsioHttpContext::NewPost(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet(builder->ComputePostBodyAsQueryString()),
      post_body);
  ahc->keep_user_agent = true;

  return ahc;
}

AsioHttpContext* UpbitRestAuth::CreateRequestByDeleteWithSignature(
    const std::string& address,
    UrlParamsBuilder* builder) {
  std::string query_string = builder->ComputeQueryString();
  std::string post_body = builder->ComputePostBody();
  std::string target = address;
  if (!query_string.empty()) {
    target += "?" + query_string;
  }
  auto ahc = AsioHttpContext::NewDelete(
      host_,
      port_,
      bind_ip_,
      target,
      GetHeaderSet(builder->ComputePostBodyAsQueryString()),
      post_body);
  ahc->keep_user_agent = true;

  return ahc;
}
